/**
 * 经纬度工具
 * 地理信息的所有计算使用这个：https://turfjs.fenxianglu.cn/category/#npm下载
 */
export default class LngAndLatUtils {
  /**
   * 获取中心点
   * Get a center latitude,longitude from an array of like geopoints
   *
   * @param data {[number,number][]} data 2 dimensional array of latitudes（纬度） and longitudes（经度）
   * For Example:
   * $data = array
   * (
   *   0 = > array(45.849382, 76.322333),
   *   1 = > array(45.843543, 75.324143),
   *   2 = > array(45.765744, 76.543223),
   *   3 = > array(45.784234, 74.542335)
   * );
   * @returns {boolean|[number,number]}
   */
  static getCenterFromDegrees(data: [number, number][] = []): [number, number] | boolean {
    if (!(data.length > 0)) {
      return false;
    }
    let lon;
    let lat;
    const num_coords = data.length;
    let X = 0.0;
    let Y = 0.0;
    let Z = 0.0;
    for (let i = 0; i < data.length; i++) {
      lat = data[i][0] * Math.PI / 180;
      lon = data[i][1] * Math.PI / 180;
      const a = Math.cos(lat) * Math.cos(lon);
      const b = Math.cos(lat) * Math.sin(lon);
      const c = Math.sin(lat);
      X += a;
      Y += b;
      Z += c;
    }
    X /= num_coords;
    Y /= num_coords;
    Z /= num_coords;
    lon = Math.atan2(Y, X);
    const hyp = Math.sqrt(X * X + Y * Y);
    lat = Math.atan2(Z, hyp);
    const newX = (lat * 180 / Math.PI);
    const newY = (lon * 180 / Math.PI);
    return [newX, newY];
  }

  /**
   * 一个经纬度实际距离差不多是 111KM
   * @param pos
   * @param pos2
   * @returns {number}
   */
  static actualLength(pos: [number, number], pos2: [number, number]): number {
    return Math.sqrt(Math.pow(pos[0] - pos2[0], 2) + Math.pow(pos[1] - pos2[1], 2)) * 111
  }

  /**
   * 地图缩放
   * @param actualLength {number}
   * @returns {number}
   */
  static mapZoom(actualLength: number): number {
    let zoom = 0
    if (actualLength <= 0.010) {
      zoom = 19
    } else if (actualLength <= 0.010) {
      zoom = 19
    } else if (actualLength <= 0.025) {
      zoom = 18
    } else if (actualLength <= 0.050) {
      zoom = 17
    } else if (actualLength <= 0.0100) {
      zoom = 16
    } else if (actualLength <= 0.0200) {
      zoom = 15
    } else if (actualLength <= 0.0500) {
      zoom = 14
    } else if (actualLength <= 1) {
      zoom = 13
    } else if (actualLength <= 2) {
      zoom = 12
    } else if (actualLength <= 5) {
      zoom = 11
    } else if (actualLength <= 10) {
      zoom = 10
    } else if (actualLength <= 20) {
      zoom = 9
    } else if (actualLength <= 30) {
      zoom = 8
    } else if (actualLength <= 50) {
      zoom = 7
    } else if (actualLength <= 100) {
      zoom = 6
    } else if (actualLength <= 200) {
      zoom = 5
    } else if (actualLength <= 500) {
      zoom = 4
    } else if (actualLength <= 1000) {
      zoom = 3
    } else if (actualLength <= 1000) {
      zoom = 3
    }
    return zoom
  }

  /**
   *
   * @param pos {[number,number]}
   * @param posList {[number,number][]}
   * @returns {number}
   */
  static posToPosMaxLength(pos: [number, number], posList: [number, number][] = []): number {
    let hyp = 0
    posList.forEach(line => {
      const number = LngAndLatUtils.actualLength(pos, line);
      hyp = Math.max(hyp, number)
    })
    return hyp
  }

  /**
   * 设置高德地图的缩放和居中
   *            posList.push([o.getPosition().lat,o.getPosition().lng])
   * @param posList 所有点位
   * @param map 地图实例
   * @param zoomOffset 缩放偏移大小
   */
  static mapCenterAndZoomByGaoDe(posList: [number, number][] = [], map: any, zoomOffset: number = 3): void {
    if (posList.length > 1) {
      const centerPos = LngAndLatUtils.getCenterFromDegrees(posList)
      let hyp = LngAndLatUtils.posToPosMaxLength(centerPos as [number, number], posList)
      const zoom = LngAndLatUtils.mapZoom(hyp) + zoomOffset
      map.setZoom(zoom);
      map.setCenter([centerPos[1], centerPos[0]]);
    } else if (posList.length === 1) {
      map.setZoom(60);
      map.setCenter([posList[0][1], posList[0][0]]);
    }
  }

  //转换常数
  static x_pi = 3.14159265358979324 * 3000.0 / 180.0
  static pi = 3.14159265358979324
  static a = 6378245.0
  static ee = 0.00669342162296594323

  static transformLon(x, y) {
    let ret = 300.0 + x + 2.0 * y + 0.1 * x * x + 0.1 * x * y + 0.1 * Math.sqrt(Math.abs(x));
    ret += (20.0 * Math.sin(6.0 * x * this.pi) + 20.0 * Math.sin(2.0 * x * this.pi)) * 2.0 / 3.0;
    ret += (20.0 * Math.sin(x * this.pi) + 40.0 * Math.sin(x / 3.0 * this.pi)) * 2.0 / 3.0;
    ret += (150.0 * Math.sin(x / 12.0 * this.pi) + 300.0 * Math.sin(x / 30.0 * this.pi)) * 2.0 / 3.0;
    return ret;
  }

  static transformLat(x, y) {
    let ret = -100.0 + 2.0 * x + 3.0 * y + 0.2 * y * y + 0.1 * x * y + 0.2 * Math.sqrt(Math.abs(x));
    ret += (20.0 * Math.sin(6.0 * x * this.pi) + 20.0 * Math.sin(2.0 * x * this.pi)) * 2.0 / 3.0;
    ret += (20.0 * Math.sin(y * this.pi) + 40.0 * Math.sin(y / 3.0 * this.pi)) * 2.0 / 3.0;
    ret += (160.0 * Math.sin(y / 12.0 * this.pi) + 320 * Math.sin(y * this.pi / 30.0)) * 2.0 / 3.0;
    return ret;
  }

// 判断是否在国内
  static outOfChina(lat, lon) {
    if (lon < 72.004 || lon > 137.8347)
      return true;
    if (lat < 0.8293 || lat > 55.8271)
      return true;
    return false;
  }

  /*
   WGS-84转换GCJ-02
  （即 天地图转高德、腾讯地图）
   */
  static wgs_gcj_encrypts(latlons) {
    let point = [];
    for (const latlon of latlons) {
      if (this.outOfChina(latlon.lat, latlon.lng)) {
        point.push({
          lat: latlon.lat,
          lng: latlon.lng
        })
        return point;
      }
      let dLat = this.transformLat(latlon.lng - 105.0, latlon.lat - 35.0);
      let dLon = this.transformLon(latlon.lng - 105.0, latlon.lat - 35.0);
      let radLat = latlon.lat / 180.0 * this.pi;
      let magic = Math.sin(radLat);
      magic = 1 - this.ee * magic * magic;
      let sqrtMagic = Math.sqrt(magic);
      dLat = (dLat * 180.0) / ((this.a * (1 - this.ee)) / (magic * sqrtMagic) * this.pi);
      dLon = (dLon * 180.0) / (this.a / sqrtMagic * Math.cos(radLat) * this.pi);
      let lat = latlon.lat + dLat;
      let lng = latlon.lng + dLon;
      point.push({
        lat: lat,
        lng: lng
      })
    }
    return point;
  }

  /*
   BD-09转换GCJ-02
   （即 百度转高德、腾讯地图）
   */
  static bd_google_encrypt(latlons) {
    let point = [];
    for (const latlon of latlons) {
      let x = latlon.lng - 0.0065;
      let y = latlon.lat - 0.006;
      let z = Math.sqrt(x * x + y * y) - 0.00002 * Math.sin(y * this.x_pi);
      let theta = Math.atan2(y, x) - 0.000003 * Math.cos(x * this.x_pi);
      let gg_lon = z * Math.cos(theta);
      let gg_lat = z * Math.sin(theta);
      point.push({
        lat: gg_lon,
        lng: gg_lat
      })
    }
    return point;
  }

  /*
   GCJ-02转换BD-09
   （即 高德、腾讯转百度地图）
   */
  static google_bd_encrypt(latlons) {
    let point = [];
    for (const latlon of latlons) {
      let x = latlon.lng;
      let y = latlon.lat;
      let z = Math.sqrt(x * x + y * y) + 0.00002 * Math.sin(y * this.x_pi);
      let theta = Math.atan2(y, x) + 0.000003 * Math.cos(x * this.x_pi);
      let bd_lon = z * Math.cos(theta) + 0.0065;
      let bd_lat = z * Math.sin(theta) + 0.006;
      point.push({
        lat: bd_lat,
        lng: bd_lon
      })
    }
    return point;
  }

  /*
   GCJ-02 到 WGS-84 的转换
   （即 高德、腾讯转天地图）
   */
  static gcj_wgs_encrypts(latlons) {
    let point = [];
    for (const latlon of latlons) {
      if (this.outOfChina(latlon.lat, latlon.lng)) {
        point.push({
          lat: latlon.lat,
          lng: latlon.lng
        })
        return point;
      }
      let dLat = this.transformLat(latlon.lng - 105.0, latlon.lat - 35.0);
      let dLon = this.transformLon(latlon.lng - 105.0, latlon.lat - 35.0);
      let radLat = latlon.lat / 180.0 * this.pi;
      let magic = Math.sin(radLat);
      magic = 1 - this.ee * magic * magic;
      let sqrtMagic = Math.sqrt(magic);
      dLat = (dLat * 180.0) / ((this.a * (1 - this.ee)) / (magic * sqrtMagic) * this.pi);
      dLon = (dLon * 180.0) / (this.a / sqrtMagic * Math.cos(radLat) * this.pi);
      let lat = dLat;
      let lng = dLon;
      point.push({
        lat: lat,
        lng: lng
      })
    }
    return point;
  }
}


